package edu.towson.cosc.cosc455.echapp1.markdowncompiler;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class MySemanticAnalyzer {

	static String name1 = "";
	static String name2 = "";
	static String name3 = "";
	static String name4 = "";
	static String name5 = "";
	static String value1 = "";
	static String value2 = "";
	static String value3 = "";
	static String value4 = "";
	static String value5 = "";
	int i = 0;
	int j = 0;

	void SemanticAnalyzer() throws CompilerException, IOException{
		ListAnalyzer();
	}
	/**
	 * This list analyzer traverses through the ArrayList used to store tokens
	 * When it finds things that are related to the define and use
	 * syntaxes, it either removes them or sets the variable name to a particular value
	 * At the end, if an END tag other than end use is found, it runs another method.
	 * @throws CompilerException
	 */
	void ListAnalyzer() throws CompilerException{
		for(i = 0; i < Compiler.StandardList.size(); i++){
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#START DEFINE")){
				Compiler.StandardList.remove(i);
				Compiler.StandardList.trimToSize();
			}
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#NAME")){
					Compiler.StandardList.remove(i);
					Compiler.StandardList.trimToSize();
				if(name1.isEmpty()){
					name1 = Compiler.StandardList.get(i);
				}
				else if(name2.isEmpty()){
					name2 = Compiler.StandardList.get(i);
				}
				else if(name3.isEmpty()){
					name3 = Compiler.StandardList.get(i);
				}
				else if(name4.isEmpty()){
					name4 = Compiler.StandardList.get(i);
				}
				else if(name5.isEmpty()){
					name5 = Compiler.StandardList.get(i);
				}
				else{
					String message = "We have too many variables in this file";
					throw new CompilerException(message);
				}
				Compiler.StandardList.remove(i);
				Compiler.StandardList.trimToSize();
				}
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#VALUE")){
					Compiler.StandardList.remove(i);
					Compiler.StandardList.trimToSize();
					if(value1.isEmpty()){
						value1 = Compiler.StandardList.get(i);
					}
					else if(value2.isEmpty()){
						value2 = Compiler.StandardList.get(i);
					}
					else if(value3.isEmpty()){
						value3 = Compiler.StandardList.get(i);
					}
					else if(value4.isEmpty()){
						value4 = Compiler.StandardList.get(i);
					}
					else if(value5.isEmpty()){
						value5 = Compiler.StandardList.get(i);
					}
					else{
						String message = "Too many variables!";
						throw new CompilerException(message);
					}
					Compiler.StandardList.remove(i);
					Compiler.StandardList.trimToSize();
				}
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#END DEFINE")){
				Compiler.StandardList.remove(i);
				Compiler.StandardList.trimToSize();
			}
			
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#START USE")){
				Compiler.StandardList.remove(i);
				Compiler.StandardList.trimToSize();
				VariableUser(i);
			}
			
			if(Compiler.StandardList.get(i).equalsIgnoreCase("#END USE")){
				Compiler.StandardList.remove(i);
				Compiler.StandardList.trimToSize();
			}
			if(Compiler.StandardList.get(i).regionMatches(true, 0, "#END " , 0, 5) &&
					!Compiler.StandardList.get(i).equalsIgnoreCase("#END USE")){
				VariableManager();
			}
		}
			
	}
		
		
	/**
	 * Starting with the variable that would have been filled in
	 * Most recently, it checks to see if the variable name would have
	 * Matched the name of another variable already defined
	 * If so, it assumes this is static scoping and deletes the more recent instance
	 */
	private void VariableManager() {
		if(!name5.isEmpty()){
			if(name5.equalsIgnoreCase(name4)){
				name5 = "";
				value5 = "";
			}
			else if(name5.equalsIgnoreCase(name3)){
				name5 = "";
				value5 = "";
			}
			else if(name5.equalsIgnoreCase(name2)){
				name5 = "";
				value5 = "";
			}
			else if(name5.equalsIgnoreCase(name1)){
				name5 = "";
				value5 = "";
			}
			return;
		}
		if(!name4.isEmpty()){
			if(name4.equalsIgnoreCase(name3)){
				name4 = "";
				value4 = "";
			}
			else if(name4.equalsIgnoreCase(name2)){
				name4 = "";
				value4= "";
			}
			else if(name4.equalsIgnoreCase(name1)){
				name4 = "";
				value4 = "";
			}
			return;
		}
		if(!name3.isEmpty()){
			if(name3.equalsIgnoreCase(name2)){
				name3 = "";
				value3 = "";
			}
			else if(name3.equalsIgnoreCase(name1)){
				name3 = "";
				value3 = "";
			}
			return;
		}
		if(!name2.isEmpty()){
			if(name2.equalsIgnoreCase(name1)){
				name2 = "";
				value2 = "";
			}
		}
	}
	/**
	 * This is the variable that is called after a start use tag is found
	 * It takes in the current position, which at the time
	 * Would contain the variable that we are intending to use.
	 * Starting with what would have been the most recently
	 * defined variable, it checks to see if the variable you
	 * wish to use matches one defined. If so, it replaces the name of the variable
	 * you wish to use with a value. Otherwise, it throws an error, as you are attempting to
	 * use a variable that you have not yet defined.
	 * @param position
	 * @throws CompilerException
	 */
	void VariableUser(int position) throws CompilerException {
		if(Compiler.StandardList.get(position).equalsIgnoreCase(name5)){
			Compiler.StandardList.set(position, value5);
		}
		else if(Compiler.StandardList.get(position).equalsIgnoreCase(name4)){
			Compiler.StandardList.set(position, value4);
			return;
		}
		else if(Compiler.StandardList.get(position).equalsIgnoreCase(name3)){
			Compiler.StandardList.set(position, value3);
			return;
		}
		else if(Compiler.StandardList.get(position).equalsIgnoreCase(name2)){
			Compiler.StandardList.set(position, value2);
			return;
		}
		else if(Compiler.StandardList.get(position).equalsIgnoreCase(name1)){
			Compiler.StandardList.set(position, value1);
			return;
		}
		else{
			String message = "You are attempting to use a variable that you have not defined!";
			throw new CompilerException(message);
		}
	}
/**
 * This method is only called directly from the compiler. The compiler passes in the filename
 * that is stored within its class. This is the path of the markdown file used, without the
 * .mkd extension. The method then creates its own file name, which is the one the compiler gave it
 * concatenated with ".html". The method then proceeds to write the html code for the markdown it was given.
 * @param name
 * @throws IOException
 */
	void WritetoFile(String name) throws IOException{
		String filename;
		filename = name.concat(".html");
		File htmlfile = new File(filename);
		FileOutputStream fstream = new FileOutputStream(htmlfile);
		OutputStreamWriter outstream = new OutputStreamWriter(fstream);
		Writer out = new BufferedWriter(outstream);
		for(j = 0; j < Compiler.StandardList.size(); j++){
			if(Compiler.StandardList.get(j).equalsIgnoreCase("#START DOCUMENT"))
				out.write("<html>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#END DOCUMENT"))
				out.write("</html>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#START HEAD"))
				out.write("<head>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end head"))
				out.write("</head>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start video")){
				out.write("<iframe src=\"" + Compiler.StandardList.get(j+1) + "\" ");
				j++;
			}
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end video"))
				out.write("/>\"");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start audio")){
				out.write("<audio controls>" + "\n" + "<source src = \"" 
			+ Compiler.StandardList.get(j+1) + "\">");
				j++;
			}
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end audio"))
				out.write("\n" + "</audio>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start title"))
				out.write("<title>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end title"))
				out.write("</title>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start paragraph"))
				out.write("<p>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end paragraph"))
				out.write("</p>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start bold"))
				out.write("<b>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end bold"))
				out.write("</b>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start italics"))
				out.write("<i>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end italics"))
				out.write("</i>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start list"))
				out.write("<ul>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end list"))
				out.write("</ul>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#newline"))
				out.write("<br>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#start item"))
				out.write("<li>");
			else if(Compiler.StandardList.get(j).equalsIgnoreCase("#end item"))
				out.write("</li>");
			else
				out.write(Compiler.StandardList.get(j));
		}
		out.close();
	}
}
